package cs340.stormtroopers.hypeerweb;

import java.util.HashSet;
import java.util.LinkedList;

public abstract class BroadcastVisitor extends Visitor
{
	private HashSet<Integer> alreadyVisitedNodes;

	public BroadcastVisitor()
	{
		alreadyVisitedNodes = new HashSet<Integer>();
	}

	public void visit(Node node, Parameters parameters)
	{
		if (node.getWebIdValue() == 0)
		{
			parameters.set("ZERO-FOUND", "true");
		}
			
		if (parameters.containsKey("ZERO-FOUND"))
		{
			HyPeerWeb.getSingleton().printToAllTracePanels("BROAD visiting "+node.getWebIdValue());
			alreadyVisitedNodes.add(node.getWebIdValue());
			operation(node, parameters);
			
			visitWholeTree(node, parameters);
		}
		else
		{
			getToZeroFromHere(node, parameters);
		}
	}

	protected abstract void operation(Node node, Parameters parameters);
	
	//visits the whole tree using the right-to-left-zeroes algorithm given in class
	private void visitWholeTree(Node node, Parameters params)
	{
		LinkedList<Node> needToBroadcast = new LinkedList<Node>();

		int me = node.getWebIdValue();

		if (node.getWebId().getHeight() > 0)
		{
			double max = Math.pow(2, node.getWebId().getHeight());

			// check each bit from right to left
			for (int currPos = 1; currPos < max; currPos *= 2)
			{
				// if current bit is 1
				if ((me & currPos) == currPos)
					break;
				// if it's a zero, switch it to a one, find the Node with that
				// webID, and add to needToBroadcast
				else
				{
					Node nodeToAdd = Node.NULL_NODE;
					int get = me | currPos;
					for (Node n : node.getConnections())
					{
						if (n.getWebIdValue() == get)
							nodeToAdd = n;
					}
					
					// TODO remove this throw?
					if (nodeToAdd != Node.NULL_NODE)
						needToBroadcast.add(nodeToAdd);
				}
			}
		}

		for (Node n : needToBroadcast)
			if(n.getWebIdValue() != me)
				n.accept(this, params);
	}

	private void getToZeroFromHere(Node node, Parameters params)
	{
		Node endCap = node.findEndCapNode();
		Node mayBeZero = endCap.getFold();
		if (mayBeZero.getWebIdValue() == 0)
		{
			mayBeZero.accept(this, params);
		}
		else
		{
			for (Node neighbor : mayBeZero.getNeighbors())
			{
				if (neighbor.getWebIdValue() == 0)
				{
					neighbor.accept(this, params);
				}
			}
		}
	}

	public static Parameters createInitialParameters()
	{
		return new Parameters();
	}
	
	public HashSet<Integer> getPath()
	{
		return alreadyVisitedNodes;
	}
	
	public String getStringPath()
	{
		StringBuilder result = new StringBuilder();
		
		for(Integer i : alreadyVisitedNodes)
		{
			result.append("[" + i.hashCode() + "] "); 
		}
		
		return result.toString();
	}
}
